package com.example.jenkinsplugins.json;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.HttpClientUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.util.ObjectUtils;

import java.io.File;
import java.io.IOException;
import java.net.ConnectException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;

/**
 * @author meihaohui
 * @version 1.0
 * @descrption desc
 * @date 2023/11/27 16:22
 */
public class JsonFileParserExample {
    
    static JsonNode all = null;
    static Set<String> rootNodes = new HashSet<>();
    static Set<Map<String, Object>> treeNodeSet = new HashSet<>();
    static TreeNode treeNode = new TreeNode("treeNode");
    static String jenkinsVersion = "2.289.1";
    
    public static void main(String[] args) {

        String filePath = System.getProperty("user.dir") + File.separator +"download"+File.separator + "plugins.json"; // 替换为你的JSON文件路径
        downJson("https://updates.jenkins.io/update-center.json?version=" + jenkinsVersion, filePath);

        try {
            String[] libraries = {
                    "git", "git-parameter", "blueocean-git-pipeline", "gitlab-plugin", "credentials", "credentials-binding",
                    "blueocean", "blueocean-pipeline-editor", "blueocean-core-js", "blueocean-pipeline-scm-api", "blueocean-dashboard",
                    "build-with-parameters", "dynamic_extended_choice_parameter", "extended-choice-parameter",
                    "list-git-branches-parameter", "kubernetes", "kubernetes-cli", "kubernetes-credentials",
                    "image-tag-parameter", "uno-choice", "workflow-aggregator", "pipeline-model-definition"
            };

            // 创建ObjectMapper对象
            ObjectMapper objectMapper = new ObjectMapper();

            // 从文件中读取JSON数据并解析
            String fileContent = new String(Files.readAllBytes(Paths.get(filePath)));
            int start = fileContent.indexOf("(")+1;
            int end = fileContent.lastIndexOf(")");
            String jsonContent = fileContent.substring(start,end);
            
            JsonNode jsonNode = objectMapper.readTree(jsonContent);
            all = jsonNode;

            for (String node : libraries) {
                findDependencies(node, treeNode);
            }
            calculateDepth(treeNode,0);

            // 创建Set<Map>并添加元素
            deleteDublicated(treeNodeSet,treeNode);
            // 将Set转换为List并根据depth排序
            List<Map<String, Object>> sortedList = new ArrayList<>(treeNodeSet);
            Collections.sort(sortedList, Comparator.comparingInt(map -> (int) map.get("depth")));
            // 输出排序后的结果
            for (Map<String, Object> map : sortedList) {
                System.out.println("Value: " + map.get("value") + ", Depth: " + map.get("depth"));
            }

            Iterator<Map<String, Object>> iterator = treeNodeSet.iterator();
            while (iterator.hasNext()) {
                Map<String, Object> cursor = iterator.next();
                String rootNodeName = cursor.get("value").toString();
                System.out.println(rootNodeName);
                if("treeNode".equals(rootNodeName)){
                    continue;
                }
                int depth = Integer.valueOf((Integer) cursor.get("depth"));
                
                String fileUrl = getHpiUrl(rootNodeName);
                if(ObjectUtils.isEmpty(fileUrl)){
                    System.out.println(rootNodeName+"--插件下载路径为空");
                    continue;
                }
                download(fileUrl,depth);
            }

            System.out.println(1);
        } catch (NullPointerException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 根据插件名称获取插件hpi文件的下载路径
     * @param pluginName
     * @return
     */
    private static String getHpiUrl(String pluginName){
        String addr = "https://updates.jenkins-ci.org";
        String prefix = "https://updates.jenkins-ci.org/download/plugins/";
        String pluginUrl = prefix+pluginName;
        String hpiUrl = "";
        // 1.生成httpclient，相当于该打开一个浏览器
        CloseableHttpClient httpClient = HttpClients.createDefault();
        CloseableHttpResponse response = null;
        // 2.创建get请求，相当于在浏览器地址栏输入 网址
        HttpGet request = new HttpGet(pluginUrl);
        // 设置请求头，将爬虫伪装成浏览器
        request.setHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50");
        request.setHeader("cookie", "SINAGLOBAL=2998252938016.661.1633674195341; SSOLoginState=1651499364; _s_tentry=login.sina.com.cn; Apache=479474709140.15967.1651499365486; ULV=1651499365516:5:1:1:479474709140.15967.1651499365486:1648431895277; UOR=,,mail.qq.com; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WhacgY0pNOEDa1uNJiTqwYm5JpX5KMhUgL.FoM0eKn0ehqf1hn2dJLoI0qLxK-L12BL1KqLxKML1h2LB-zLxKqL1-qLBoBLxKML1-2L1hBLxKMLBK-LBK-LxK-LB-BL1K5t; ALF=1683254982; SCF=ArFUroOTCUi7pZz3l9vK_fwVsZLqABLd08MI2jY6t8Grm6OpCd65xtBPuR6qb-wLJDr_Sh1ePIzDf059x3MpBqg.; SUB=_2A25Pd0sYDeRhGeFN6loS8CjJwzSIHXVsBTvQrDV8PUNbmtAKLWvekW9NQEo2vwJfqUzWmhWO6d_LQURjNZ-V5Epv");
        //        如果有ip代理，可以加上如下代码
        //        HttpHost proxy = new HttpHost(IP地址, 端口号);
        //        RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
        //        request.setConfig(config);
    
        StringBuffer sb = new StringBuffer();
        try {
            // 3.执行get请求，相当于在输入地址栏后敲回车键
            response = httpClient.execute(request);
        
            // 4.判断响应状态为200，进行处理
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                // 5.获取响应内容
                HttpEntity httpEntity = response.getEntity();
                String html = EntityUtils.toString(httpEntity, "utf-8");
                // System.out.println(html);
            
                /**
                 * 下面是Jsoup展现自我的平台
                 */
                // 6.Jsoup解析html
                Document document = Jsoup.parse(html);
                // 像js一样，通过标签获取title
                Element artifactList = document.select(".artifact-list").first();
                Elements versions = artifactList.children();
                for (Element version : versions){
                    if(version.select(".core-dependency")!=null && version.select(".core-dependency").size()>0){
                        String verNum = version.select(".core-dependency").first().text().replace("Requires Jenkins ", "");
                        String ver ;
                        if(verNum.split("\\.").length - 1 > 1){
                            ver = verNum.substring(verNum.indexOf(".")+1,verNum.lastIndexOf("."));
                        }else {
                            ver = verNum.substring(verNum.lastIndexOf(".")+1);
                        }
                        
                        // 查询jenkins的版本数字
                        String jenkinsVer;
                        if(jenkinsVersion.split("\\.").length - 1 > 1){
                            jenkinsVer = jenkinsVersion.substring(jenkinsVersion.indexOf(".")+1,jenkinsVersion.lastIndexOf("."));
                        }else {
                            jenkinsVer = jenkinsVersion.substring(jenkinsVersion.lastIndexOf(".")+1);
                        }
                        
                        // 判断版本是否小于当前Jenkins版本(只针对2版本的jenkins有效)
                        if (Integer.valueOf(ver) <= Integer.valueOf(jenkinsVer)) {
                            System.out.println(pluginName+" version "+ ver +" is ok");
                            System.out.println(addr + version.select(".version").attr("href"));
                            hpiUrl = addr + version.select(".version").attr("href");
                            break;
                        }
                    }
                }
                return hpiUrl;
            } else {
                // 如果返回状态不是200，比如404（页面不存在）等，根据情况做处理，这里略
                System.out.println("返回状态不是200");
                System.out.println(EntityUtils.toString(response.getEntity(), "utf-8"));
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6.关闭
            HttpClientUtils.closeQuietly(response);
            HttpClientUtils.closeQuietly(httpClient);
            return hpiUrl;
        }
    }
    
    /**
     * 下载特定jenkins的依赖插件信息文件
     * @param jsonUrl
     */
    private static void downJson(String jsonUrl, String destinationPath){

        try {
            // 使用java.net.URL打开连接
            System.out.println("下载路径是："+jsonUrl);
            URL url = new URL(jsonUrl);

            File dir = new File(destinationPath);
            dir.mkdirs();
            Files.copy(url.openStream(), Paths.get(destinationPath), StandardCopyOption.REPLACE_EXISTING);

            System.out.println(jsonUrl.substring(jsonUrl.lastIndexOf("/") + 1)+" downloaded successfully.");

        } catch (ConnectException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 下载插件
     * @param fileUrl
     * @param depth
     */
    private static void download(String fileUrl,int depth) {
        System.out.println("下载路径是："+fileUrl + "depth: "+depth);
        String destinationPath = System.getProperty("user.dir") + File.separator +"download"+File.separator+jenkinsVersion;
        String depthPath = destinationPath+ File.separator + String.format("%02d", depth);
        String fileName = fileUrl.substring(fileUrl.lastIndexOf("/") + 1,fileUrl.lastIndexOf("."))+".jpi";
        String filePath = depthPath + File.separator + fileName;

        try {
            // 使用java.net.URL打开连接
            URL url = new URL(fileUrl);

            File directory = new File(depthPath);
            directory.mkdirs();

            Files.copy(url.openStream(), Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);

            System.out.println(fileUrl.substring(fileUrl.lastIndexOf("/") + 1)+" downloaded successfully.");

        } catch (ConnectException e){
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 寻找插件的依赖（查询叶子节点）
     * @param nodeName
     * @param tree
     */
    private static void findDependencies(String nodeName, TreeNode tree) {
        TreeNode leavf = new TreeNode(nodeName, 0);
        
        Iterator<JsonNode> elements = all.get("plugins").get(nodeName).get("dependencies").elements();
        
        if (!rootNodes.contains(nodeName)) {
            System.out.println(nodeName);
            rootNodes.add(nodeName);
        }
        
        while (elements.hasNext()) {
            String childName = elements.next().get("name").asText();
            findDependencies(childName, leavf);
        }
        tree.addChild(leavf);
    }
    
    /**
     * 计算节点深度
     * @param node
     * @param depth
     */
    private static void calculateDepth(TreeNode node, int depth) {
        if (node.getChildren().size() == 0) {
            return ;
        }
        int childDepth = 0;
        for (TreeNode child : node.getChildren()) {
            calculateDepth(child, depth + 1);
            if(child.getDepth()>childDepth){
                childDepth = child.getDepth();
            }
        }
        node.setDepth(childDepth+1);
    }
    
    /**
     * 剔除重复节点名称
     * @param treeNodeSet
     * @param treeNode
     */
    private static void deleteDublicated(Set<Map<String, Object>> treeNodeSet,TreeNode treeNode){
        // 先加自己
        TreeNode.addTreeNodeToSet(treeNodeSet,treeNode);
        
        if (treeNode.getChildren().size() == 0) {
            return ;
        }
        // 再加子节点
        for (TreeNode child : treeNode.getChildren()) {
            deleteDublicated(treeNodeSet,child);
        }
    }
}
